﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Linq.Expressions;
namespace SLSudoDB
{
    public class Table<T> : IEnumerable<T> where T : class,IRecord
    {
        IFileStorageProvider fsProvider;
        const string DATA_DIR = @"db\";
        protected string tableName;
        protected string tableDirectory;
        private string recordTemplate;
        protected Expression limitingExpression;
        protected string RecordPathTemplate
        {
             get
            {
                return recordTemplate;
            }
            private set
            {
                recordTemplate = value;
            }
        }
        private int? lastIssuedKey;
        private string indexFile;
        
        private int GetNextKey()
        {
            int value;
            if (lastIssuedKey.HasValue)
                fsProvider.WriteAllText(this.indexFile, (++lastIssuedKey).Value.ToString());
            else
                if (this.fsProvider.FileExists(this.indexFile))
                    if (int.TryParse(this.fsProvider.ReadAllText(this.indexFile), out value))
                    {
                        lastIssuedKey = value + 1;
                        this.fsProvider.WriteAllText(this.indexFile, lastIssuedKey.Value.ToString());
                    }
                    else
                    {
                        fsProvider.WriteAllText(this.indexFile, "1");
                        lastIssuedKey = 1;
                    }
                else
                {
                    fsProvider.WriteAllText(this.indexFile, "1");
                    lastIssuedKey = 1;
                }
            return lastIssuedKey.Value;
        }
        public Table()
        {
            this.fsProvider = new IsolatedStorageProvider();

            tableName = typeof(T).ToString();
            tableDirectory = DATA_DIR + tableName +@"\";
            RecordPathTemplate = tableDirectory + "{0}.xml";
            ensurePath(tableDirectory);
        }
        protected void ensurePath(string dirPath)
        {
            if (!this.fsProvider.DirectoryExists(dirPath))
                this.fsProvider.CreateDirectory(dirPath);
            this.indexFile = dirPath + "index";
        }

        public void Delete(T item)
        {
            
            if(recordExists(item.PrimaryKey.ToString()))
                this.fsProvider.DeleteFile(string.Format(RecordPathTemplate,item.PrimaryKey));
        }
        public T Update(T item)
        {
            if(recordExists(item.PrimaryKey.ToString()))
                writeRecord(item);
            return item;
        }
        public T Insert(T item)
        {
            item.PrimaryKey = GetNextKey();
            if (!recordExists(item.PrimaryKey.ToString()))
                writeRecord(item);
            else
                throw new Exception("Can't create item due to primary key already existing");
            return item;
        }

        /// <summary>
        /// tests to see if this primary key has a record in the file store
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        protected bool recordExists(string id)
        {
            return this.fsProvider.FileExists(string.Format(RecordPathTemplate, id));
        }


        protected T loadRecord(int id)
        {
            
            if (recordExists(id.ToString()))
                return this.Deserialize<T>(string.Format(RecordPathTemplate, id));
            else
                throw new Exception(string.Format("Primary key: {0} not found in table {1}", id.ToString(), tableName));
        }
        protected T loadRecord(string id)
        {
           
            if (recordExists(id))
                return this.Deserialize<T>(string.Format(RecordPathTemplate, id));
            else
                throw new Exception(string.Format("Primary key: {0} not found in table {1}", id.ToString(), tableName));
        }
        protected void writeRecord(T recordToWrite) {
            this.Serialize<T>(recordToWrite,string.Format(RecordPathTemplate,recordToWrite.PrimaryKey));
        }

        /// <summary>
        /// this method uses basic deserialization to create an object from an xml file
        /// </summary>
        /// <typeparam name="TResult">type of object to create</typeparam>
        /// <param name="fileName"></param>
        /// <returns></returns>
        protected TResult Deserialize<TResult>(string fileName) where TResult : class
        {
            XmlSerializer ser = new XmlSerializer(typeof(TResult));
            StreamReader reader = fsProvider.GetStreamReader(fileName);
            var record = ser.Deserialize(reader) as TResult;
            reader.Close();
            return record;
        }

        /// <summary>
        /// this method uses basic serialization to save objects to the FS
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="objToStore"></param>
        /// <param name="fileName"></param>
        protected void Serialize<TResult>(object objToStore,string fileName) where TResult : class
        {
            XmlSerializer ser = new XmlSerializer(typeof(TResult));
            StreamWriter writer = this.fsProvider.GetStreamWriter(fileName);
            ser.Serialize(writer, objToStore);
            writer.Close();
        }


        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            List<T> items = new List<T>();
            var files = this.fsProvider.GetFilesInDirectory(tableDirectory, "*.xml");
            foreach (var file in files)
                items.Add(loadRecord(file.Replace(".xml", "")));
            return items.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            List<T> items = new List<T>();
            var files = this.fsProvider.GetFilesInDirectory(tableDirectory, "*.xml");
            foreach (var file in files)
                items.Add(loadRecord(file.Replace(".xml", "")));
            return items.GetEnumerator();
        }

        #endregion
    }
}
